<script setup lang="ts">
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
import { checkCode, getCode, joinToImgBase64 } from '@/components/machine-verification/hooks'
import type { machineVerifyGetCodeData } from '@/components/machine-verification/hooks/request-types'
import { getFullPuzzleSize } from '@/components/machine-verification/hooks/getFullPuzzleSize'
import { aesEncrypt } from '@/components/machine-verification/hooks/signBySecretKey'
interface Props {
  visible: boolean
}
const props = withDefaults(defineProps<Props>(), {})

const emits = defineEmits<{
  (e: 'update:visible', val: boolean): void
  (e: 'ok', val: string): void
}>()

const open = computed({
  get() {
    return props.visible
  },
  set(val) {
    emits('update:visible', val)
  }
})
const handleOk = () => {}
// 拼图背景
const fullPuzzleRef = ref()
// 拼图
const puzzleRef = ref()
// 滑块
const sliderRef = ref()
// 轨道
const trackRef = ref()

const machineVerifyState = ref<machineVerifyGetCodeData>()

// 拼图盒子的初始宽高
const puzzleBoxState = reactive({
  width: 310,
  height: 155
})
// 拼图块的初始宽高
const puzzleBlockState = reactive({
  width: 81,
  height: 155
})

// 轨道上拖拽块的宽度
const sliderBlockState = {
  width: 35
}

// 记录拖拽信息
const dragState = reactive({
  isDrag: false,
  startX: 0,
  curLeft: 0,
  maxLeft: 0
})

// 用于现实验证消息的元素
const msgRef = ref()
async function init() {
  dragState.isDrag = false
  dragState.startX = 0
  dragState.curLeft = 0
  dragState.maxLeft = 0
  sliderRef.value.style.left = 0
  puzzleRef.value.style.left = 0
  msgRef.value.innerHTML = ''
  msgRef.value.className = 'verify-msg'

  machineVerifyState.value = await getCode()
  const puzzleBoxSize = await getFullPuzzleSize(machineVerifyState.value.originalImageBase64)
  puzzleBoxState.width = puzzleBoxSize.width
  puzzleBoxState.height = puzzleBoxSize.height

  const puzzleBlockSize = await getFullPuzzleSize(machineVerifyState.value.jigsawImageBase64)
  puzzleBlockState.width = puzzleBlockSize.width
  puzzleBlockState.height = puzzleBlockSize.height

  dragState.maxLeft = puzzleBoxState.width - puzzleBlockSize.width
  bindEvent()
}

// 处理显示验证结果
const handleVerifyMsg = (flag: boolean) => {
  if (flag) {
    msgRef.value.innerHTML = '验证成功!'
    msgRef.value.className = 'verify-msg success'
  } else {
    msgRef.value.innerHTML = '验证失败'
    msgRef.value.className = 'verify-msg fail'
  }
}

const handleMouseDown = () => {
  if (dragState.isDrag) return
  const event = window.event as any
  dragState.isDrag = true
  dragState.startX = event.clientX || event.touches[0].clientX
  window.addEventListener('touchmove', handleMouseMove)
  window.addEventListener('touchend', handleMouseUp)
  window.addEventListener('mousemove', handleMouseMove)
  window.addEventListener('mouseup', handleMouseUp)
}
const handleMouseMove = () => {
  const event = window.event as any
  if (dragState.isDrag) {
    const left = (event.clientX || event.touches[0].clientX) - dragState.startX
    if (left < 0) {
      sliderRef.value.style.left = 0
      puzzleRef.value.style.left = 0
      dragState.curLeft = 0
    } else if (left > dragState.maxLeft) {
      sliderRef.value.style.left = dragState.maxLeft + 'px'
      puzzleRef.value.style.left = dragState.maxLeft + 'px'
      dragState.curLeft = dragState.maxLeft
    } else {
      sliderRef.value.style.left = left + 'px'
      puzzleRef.value.style.left = left + 'px'
      dragState.curLeft = left
    }
  }
}
const handleMouseUp = async () => {
  if (dragState.isDrag) {
    unbindEvent()
    const pointJson = aesEncrypt(
      JSON.stringify({ x: dragState.curLeft, y: 5.0 }),
      machineVerifyState.value?.secretKey
    )
    const checkResult = await checkCode(pointJson)
    handleVerifyMsg(!!checkResult)
    if (checkResult) {
      emits('ok', `${checkResult}`)
      open.value = false
    } else {
      setTimeout(() => {
        dragState.isDrag = false
        handleReload()
      }, 2000)
    }
  }
}

const handleReload = () => {
  if (dragState.isDrag) return
  else {
    init()
  }
}
onMounted(() => {
  console.log('mounted')
})
watch(open, (val) => {
  console.log('watch')
  val &&
    nextTick(() => {
      init()
    })
})

//绑定事件
function bindEvent() {
  puzzleRef.value.addEventListener('touchstart', handleMouseDown)
  sliderRef.value.addEventListener('touchstart', handleMouseDown)
  puzzleRef.value.addEventListener('mousedown', handleMouseDown)
  sliderRef.value.addEventListener('mousedown', handleMouseDown)
}
//解绑事件
function unbindEvent() {
  puzzleRef.value.removeEventListener('touchstart', handleMouseDown)
  sliderRef.value.removeEventListener('touchstart', handleMouseDown)
  puzzleRef.value.removeEventListener('mousedown', handleMouseDown)
  sliderRef.value.removeEventListener('mousedown', handleMouseDown)
  window.removeEventListener('touchmove', handleMouseMove)
  window.removeEventListener('touchend', handleMouseUp)
  window.removeEventListener('mousemove', handleMouseMove)
  window.removeEventListener('mouseup', handleMouseUp)
}
</script>
<template>
  <a-modal
    v-model:open="open"
    :width="`${puzzleBoxState.width + 90}px`"
    centered
    destroy-on-close
    @ok="handleOk"
    :footer="null"
    class="verify-modal"
  >
    <div class="tips">请完成人机验证</div>
    <div class="verify-box">
      <!-- 拼图盒子-->
      <div
        class="puzzle-box"
        :style="{
          width: `${puzzleBoxState.width}px`,
          height: `${puzzleBoxState.height}px`
        }"
      >
        <img src="./img/reload.png" alt="" class="reload" draggable="false" @click="handleReload" />
        <img
          class="full-puzzle"
          ref="fullPuzzleRef"
          draggable="false"
          alt=""
          :src="joinToImgBase64(machineVerifyState?.originalImageBase64)"
        />
        <img
          class="puzzle"
          ref="puzzleRef"
          draggable="false"
          alt=""
          :src="joinToImgBase64(machineVerifyState?.jigsawImageBase64)"
        />
        <div class="verify-msg" ref="msgRef"></div>
      </div>
      <!-- 拖拽条-->
      <div
        class="drag-bar"
        :style="{
          width: `${sliderBlockState.width + (puzzleBoxState.width - puzzleBlockState.width)}px`
        }"
      >
        <div class="track" ref="trackRef">拖动滑块验证</div>
        <div
          class="slider"
          ref="sliderRef"
          :style="{
            width: `${sliderBlockState.width}px`
          }"
        >
          <i></i>
          <i></i>
          <i></i>
        </div>
      </div>
    </div>
    <template #title> </template>
    <template #closeIcon> </template>
  </a-modal>
</template>

<style scoped lang="scss">
.verify-box {
  .puzzle-box {
    position: relative;
    margin: 0 auto !important;
    overflow: hidden;
    .full-puzzle {
      user-select: none;
    }
    .puzzle {
      position: absolute;
      top: 0;
      left: 0;
      user-select: none;
      cursor: grab;
    }
    .reload {
      position: absolute;
      width: 30px;
      top: 10px;
      right: 10px;
      user-select: none;
    }
    .verify-msg {
      width: 100%;
      position: absolute;
      z-index: 1;
      left: 0;
      bottom: 0;
      text-align: center;
      font-size: 13px;
      color: #fff;
      transform: translateY(100%);
      transition: all 0.3s;
      user-select: none;
      &.fail,
      &.success {
        transform: translateY(0);
      }
      &.fail {
        background-color: #83ce3f;
      }
      &.success {
        background-color: #84cb44;
      }
    }
  }
  .drag-bar {
    position: relative;
    margin: 20px auto !important;
    height: 35px;
    .track {
      background-color: #eef1f8;
      box-shadow: 0 0 4px #ccc;
      color: #b7bcd1;
      height: 100%;
      text-align: center;
      line-height: 35px;
      user-select: none;
    }
    .slider {
      position: absolute;
      top: 0;
      left: 0;
      height: 100%;
      cursor: grab;
      background-color: #fff;
      border-radius: 3px;
      box-shadow: 0 0 4px #ccc;
      display: flex;
      align-items: center;
      justify-content: center;
      & > i {
        height: 40%;
        transition: all 0.2s;
        border: solid 1px #6aa0ff;
        margin: 0 2px;
      }
      &:hover i:nth-child(1) {
        border: solid 4px transparent;
        height: 0;
        border-right-color: #6aa0ff;
      }
      &:hover i:nth-child(2) {
        width: 8px;
        height: 8px;
        border-radius: 50%;
        background-color: #6aa0ff;
      }
      &:hover i:nth-child(3) {
        border: solid 4px transparent;
        height: 0;
        border-left-color: #6aa0ff;
      }
    }
  }
}
.tips {
  text-align: center;
  margin-bottom: 10px;
  font-size: 16px;
  user-select: none;
}
</style>
<style lang="scss">
.verify-modal .ant-modal-content {
  padding: 10px 0 20px;
  transition: all 0.3s ease;
}
</style>
